;; The first three lines of this file were inserted by DrRacket. They record metadata
;; about the language level of this file in a form that our tools can easily process.
#reader(lib "htdp-beginner-reader.ss" "lang")((modname hwk1) (read-case-sensitive #t) (teachpacks ()) (htdp-settings #(#t constructor repeating-decimal #f #t none #f ())))
#|
Program-series: Lots Of Irritating And Silly Parentheses (L.I.S.P.)
Program: Homework 1: Warming Up in Racket
Author: Henrique "Nican" Polido

   An attempt of some form to create an SVN/Mercury
   This program create patches with operations that can be applied to strings to modifify them in certain ways

|#

;;An insert-operation is (string)
;;This operation will insert the given string at the revision
(define-struct insert-operation( data ) )

;;An delete-operation is (number)
;;This operation will remove the given the number of charcters
(define-struct delete-operation( length ) )

;;An replace-operation is ( string number)
;;This operation will do both delete and insert at the same position
(define-struct replace-operation( data length ) )

(define EXEMPLE-DEL-OPERATION (make-delete-operation 3))
(define EXEMPLE-INS-OPERATION (make-insert-operation "TestTest"))
(define EXEMPLE-REPLACE-OPERATION (make-replace-operation "Nican" 5))

;;operation-lenght: operation->number
;;Returns the number of affected charcters of the operation after the initial position
(check-expect (operation-length EXEMPLE-DEL-OPERATION) 3)
(check-expect (operation-length EXEMPLE-INS-OPERATION) 8)
(check-expect (operation-length EXEMPLE-REPLACE-OPERATION) 5)

(define (operation-length op)
  (cond [(insert-operation? op) 
           (string-length (insert-operation-data op))
        ]
        [(delete-operation? op) 
           (delete-operation-length op)
        ]
        [(replace-operation? op)
           (max (string-length (replace-operation-data op)) (replace-operation-length op) )
        ]
  )
)

;;A patch to be applied to a document
;;A patch is (number patch-operation)
(define-struct patch( position operation ) )

(define EXEMPLE-DEL-PATCH (make-patch 3 EXEMPLE-DEL-OPERATION))
(define EXEMPLE-INS-PATCH (make-patch 3 EXEMPLE-INS-OPERATION))
(define EXEMPLE-REPLACE-PATCH (make-patch 1 EXEMPLE-REPLACE-OPERATION))

;;apply-insert: string number string -> string
;;Insers the "data" into the "input" at the given "position"
(check-expect (apply-insert "abcdef" 6 "123") "abcdef123")
(check-expect (apply-insert "abcdef" 3 "123") "abc123def")
(check-expect (apply-insert "abcdef" 0 "123") "123abcdef")

(define (apply-insert input position data)
      (format "~a~a~a" (substring input 0 position) data (substring input position))
)

;;apply-insert: string number number -> string
;;Deletes the "given" number of bytes after the position on the input string
(check-expect (apply-delete "abcdef" 3 3) "abc")
(check-expect (apply-delete "abcdef" 0 3) "def")
(check-expect (apply-delete "abcdef" 0 0) "abcdef")

(define (apply-delete input position given)
      (format "~a~a" (substring input 0 position) (substring input (+ given position)))
)

;;apply-op operation string number -> string
;;Applies the given operation at the input string at the given position

(check-expect (apply-op EXEMPLE-INS-OPERATION "ABCDEF" 3) "ABCTestTestDEF" )
(check-expect (apply-op EXEMPLE-DEL-OPERATION "ABCDEFGHI" 3) "ABCGHI" )
(check-expect (apply-op EXEMPLE-REPLACE-OPERATION "1234567890" 1) "1Nican7890" )

(define (apply-op operation input position )
     (cond [(insert-operation? operation) 
              (apply-insert input position (insert-operation-data operation))
           ]   
           [(delete-operation? operation) 
              (apply-delete input position (delete-operation-length operation))
           ]
           [(replace-operation? operation)
              (apply-insert 
                 (apply-delete input position (replace-operation-length operation)) 
                 position
                 (replace-operation-data operation)
              )
           ]
      )
)

;;apply-patch: string patch -> string
;;Consumes the input string and a patch structure and outputs the string with the applied modifications
(check-expect (apply-patch "ABCDEF" EXEMPLE-INS-PATCH) "ABCTestTestDEF" )
(check-expect (apply-patch "ABCDEFGHI" EXEMPLE-DEL-PATCH) "ABCGHI" )
(check-expect (apply-patch "1234567890" EXEMPLE-REPLACE-PATCH) "1Nican7890" )

(define (apply-patch input patch)
   (apply-op (patch-operation patch) input (patch-position patch) )
)

;;overlap?: patch patc2 -> boolean
;;Confirms if the two patchs will not overlap

(check-expect (overlap? (make-patch 3 (make-insert-operation "Hello!")) (make-patch 4 (make-insert-operation "Hello!"))) false )
(check-expect (overlap? (make-patch 100 (make-insert-operation "Hello!")) (make-patch 4 (make-insert-operation "Hello!"))) false)
(check-expect (overlap? (make-patch 0 (make-delete-operation 10)) (make-patch 9 (make-insert-operation "Hello!"))) true )
(check-expect (overlap? (make-patch 1 (make-delete-operation 1)) (make-patch 1 (make-delete-operation 1))) true )
(check-expect (overlap? (make-patch 0 (make-replace-operation "Nican" 10)) (make-patch 3 (make-insert-operation "Hello!"))) true )
(check-expect (overlap? (make-patch 5 (make-replace-operation "Nican" 5)) (make-patch 0 (make-delete-operation 2))) false )

(define (overlap? patch1 patch2)
  (cond [(and (insert-operation? (patch-operation patch1)) (insert-operation? (patch-operation patch2)))
          (= (patch-position patch1) (patch-position patch2) )
        ]
        [(> (patch-position patch1) (patch-position patch2)) 
          (overlap?-ordered patch2 patch1)
        ]
        [ else 
          (overlap?-ordered patch1 patch2)
        ]
   )
)

;;INTERNAL USE - Do not use.
;;Assuming that the position of patch1 is before the position of patch2
;;Checks if the two items overlap
(define (overlap?-ordered patch1 patch2)
    (< (patch-position patch2) (+ (patch-position patch1) (operation-length (patch-operation patch1))))
)

;;merge: String patch patch -> String
;;Merges two patches into the the final string

(check-expect (merge "1234567890" (make-patch 3 (make-insert-operation "Hello!")) (make-patch 0 (make-delete-operation 3))) "Hello!4567890" )
(check-expect (merge "1234567890" (make-patch 4 (make-delete-operation 4)) (make-patch 0 (make-delete-operation 3))) "490" )
(check-expect (merge "1234567890" (make-patch 1 (make-insert-operation "Hello!")) (make-patch 0 (make-delete-operation 3))) false )
(check-expect (merge "1234567890" (make-patch 0 (make-delete-operation 8)) (make-patch 9 (make-insert-operation "Hello!"))) "9Hello!0" )

(define (merge input patch1 patch2)
   (cond [(overlap? patch1 patch2)
             false 
         ]
         [ ( > (patch-position patch1) (patch-position patch2)  )
             (apply-patch (apply-patch input patch1) patch2)
         ]
         [ else
             (apply-patch (apply-patch input patch2) patch1)
         ]
   )
 )

#|
  Question 6:
  It is better to return false over returning the original string
  Because the user can easily verify if the there are problems with the merging
  And cause errors, instead of running on code that did not do it's job
|#

(define ORIGINAL-SHAKESPEARE "Hamlet: Do you see yonder cloud that's almost in shape of a camel?
Polonius: By the mass, and 'tis like a camel, indeed.
[...]
Hamlet: Or like a whale?
Polonius: Very like a whale.")

(define SHAKESPEARE-PATCH1 (make-patch 162 (make-replace-operation "It's totall" 3)))
(define SHAKESPEARE-PATCH2 (make-patch 80  (make-replace-operation "golly, it " 16)))
(define SHAKESPEARE-PATCH3 (make-patch 46  (make-replace-operation "the" 2)))
(define SHAKESPEARE-PATCH4 (make-patch 19  (make-replace-operation "the cloud over there" 12)))

(define (modernize input)
  (apply-patch (apply-patch (apply-patch (apply-patch input SHAKESPEARE-PATCH1) SHAKESPEARE-PATCH2) SHAKESPEARE-PATCH3) SHAKESPEARE-PATCH4)
) 
 
(check-expect (modernize ORIGINAL-SHAKESPEARE) "Hamlet: Do you see the cloud over there that's almost the shape of a camel?
Polonius: By golly, it is like a camel, indeed.
[...]
Hamlet: Or like a whale?
Polonius: It's totally like a whale.")



#|
8.
(/ (- (* 9 3) (double a)) 2)
(/ (- 27 (double a)) 2)
"a: name is not defined, not a parameter, and not a primitive name"

9.
(or (< 5 2) (and (= 15 (- 18 3)) (> 8 4)))
(or false (and (= 15 (- 18 3)) (> 8 4)))
(or false (and (= 15 15) (> 8 4)))
(or false (and true (> 8 4)))
(or false (and true true))
(or false true)
true

10.
(and (+ 5 -1) false)
"and: question result is not true or false: 4"

11. (apply-patch 'remove "this is a test string")
Wait, what am I deleting? How many charcters, and where?
How does your symbol 'remove contains the position and the operation? as explained before question 1?

12.
(cond [(true) 5]
      [(true)]
      )
A condition that has no return value.

13. (I had to fail a bit too hard on LISP before I could pull this error off)
(string=? x "HELLO")
(define x "HELLO")
Use of a variable before it was declared

14. 
(5)
A number is not a function

|#